import random;
from Activity import Activity;

class BlackBoard(object):
    def __init__(self, inputSim):
        self.stgm = inputSim.stgm;
        self.graph = self.stgm.Graph();
        self.inputSim = inputSim;
        self.aStates = inputSim.aStates;
        self.aActs_Time = dict();
        self.aKeys_Time = [];
    
    def getIndex(self, aAct):
        for i in range(len(self.aStates)): #For each State
            bMatch = True;
            if aAct[0] == self.aStates[i][0]: #If has same Activity name
                if len(aAct[1])!=len(self.aStates[i][1]): #If has Diff # Args
                    bMatch = False;
                for j in range(len(aAct[1])): #For each Arg element
                    if ((aAct[1][j]=="*" and self.aStates[i][1][j]!="*") or
                        (aAct[1][j]!="*" and self.aStates[i][1][j]=="*")):
                        bMatch = False;
                if bMatch==True:
                    return i;
        return -1;
    
    def getMatches(self, aActT0, actT1):
        aMatches = [];
        for i in range(len(aActT0)): #For each Act in t-1
            for j in range(len(aActT0[i].aPersons)): #For each Arg t-1 element
                if aActT0[i].aPersons[j]=="*":
                    continue;
                bMatch = False;
                for k in range(len(actT1[1])): #For each Arg t element
                    if(aActT0[i].aPersons[j] == actT1[1][k]):
                        bMatch = True;
                        break;
                if bMatch==False:
                    break;
            if bMatch==True:
                aMatches.append([i+1, [aActT0[i].sActivity, aActT0[i].aPersons]]);
        return aMatches;
    
    def getMatch(self, actT0, actT1):
        for j in range(len(actT0[1])): #For each Arg t-1 element
            if actT0[1][j]=="*":
                continue;
            bMatch = False;
            for k in range(len(actT1[1])): #For each Arg t element
                if(actT0[1][j] == actT1[1][k]):
                    bMatch = True;
                    break;
            if bMatch==False:
                break;
        return bMatch;
            
    def getNoisyOR(self, aCondProbs):
        aCPT = [];
        nSize = len(aCondProbs);
        for i in range(2**nSize):
            binCPT = bin(i)[2:].zfill(nSize);
            nP = 1;
            for j in range(nSize):
                if binCPT[j]=="1":
                    nP *= (1-aCondProbs[j]);
            aCPT.append(1-nP);
        return aCPT;
    
    def getSegmentAct(self, actModel, nInitT, nEndT):
        #Activity:
        newAct = self.createActNode(None, actModel.rootAct, nInitT, nEndT, bRoot=True);
        #Create SubActivities:
        for childNode in actModel.aChildNodes:
            #SubActivity:
            newAct = self.createActNode(newAct, childNode, nInitT, nEndT, bRoot=False);
        print "---Created Segment Activity Node:";
        self.inputSim.printActNode(newAct);
        return newAct;
    
    def createActNode(self, actModel, actNode, nInitT, nEndT, bRoot=False):
        sActName = actNode.actName;
        sId = actNode.sId.split(".")[0] + "." + str(nEndT);
        aObjects = list(actNode.aObjects); #Each corresponds to their respective time in aObjectTimes
        aObjectTypes = list(actNode.aObjectTypes);
        if bRoot:
            aObjectTimes = [];
            aSubjects = [actNode.aSubjects[0]];
            for i in range(len(actNode.aObjectTimes)):
                aObjT = actNode.aObjectTimes[i];
                aObjectTimes.append(list(aObjT));
                nObjInit = int(aObjT[0].replace("+",""));
                nObjEnd = int(aObjT[1].replace("+",""));
                if int(nEndT)>=nObjInit and int(nEndT)<=nObjEnd:
                    aSubjects.append(aObjects[i]);
        else:
            aSubjects = list(actNode.aSubjects);
            aObjectTimes = list(actNode.aObjectTimes);
        aTimes = list(actNode.aTimes);
        aTimes[0] = str(nInitT);
        aTimes[-1] = str(nEndT);
        sParentsId = [];
        if bRoot==False:
            for parentId in actNode.sParentsId:
                pId = parentId.split(".")[0] + "." + str(nEndT);
                sParentsId.append(pId);
        aScores = list(actNode.aScores);
        sLocation = actNode.location;
        #Add the Activity or Sub-Activity
        if bRoot:
            actModel = Activity(self.stgm, sActName, aSubjects, int(nEndT), sId, True);
        actModel.makeXmlNode(sId, sActName, aSubjects, aObjects, aObjectTypes, aObjectTimes, aTimes, 
                             sParentsId, aScores, sLocation, bRoot);
        return actModel;
    
    def addActCollisions(self, actModel):
        aTimePts = []; #TimePoints to add Segments
        iAct = self.aKeys_Time.index(str(actModel.nTime)); #Index of Activity in TimeLine
        nInitT = int(actModel.rootAct.aTimes[0]); #Init Time of this Activity
        #Add TimePoints for this Action Segment
        for i in range(1,iAct+1): #Move from iAct-1 Backwards (up to nInitT)
            nEndActT = int(self.aKeys_Time[iAct-i]); #End Time in Each Segment Activity
            if (nEndActT <= nInitT):
                break;
            for actM in self.aActs_Time[str(nEndActT)]:
                if (actM.bCopySegment==False): 
                    if (nEndActT > nInitT) and not(nEndActT in aTimePts): 
                        aTimePts.append(nEndActT); #Add Segment's (End) Time Point
                    nInitActT = int(actM.rootAct.aTimes[0]);
                    if (nInitActT > nInitT) and not(nInitActT in aTimePts):
                        aTimePts.append(nInitActT); #Add Segment's (Init) Time Point
        #Add Collision Segments (from TimePoints Obtained)
        aTimePts.sort();
        print "-----Adding Segments:---------"
        print "aTimePts:", aTimePts;
        if len(aTimePts)>0:
            print [nInitT, aTimePts[0]];
            segmentAct = self.getSegmentAct(actModel, nInitT, aTimePts[0]); #Add copy of this node
            self.addActToBB(segmentAct);
        for i in range(len(aTimePts)-1):
            nInitPt = aTimePts[i];
            nEndPt = aTimePts[i+1];
            print [nInitPt, nEndPt];
            segmentAct = self.getSegmentAct(actModel, nInitPt, nEndPt); #Add copy of this node
            self.addActToBB(segmentAct);
#        self.addFurtherActCollisions(actModel);
    
    def addFurtherActCollisions(self, actModel):
        #Check Further Collisions on Other Activity Nodes (caused by this Activity)
        #Collect all those Activity Nodes (Collided Activites)
        aOtherActs_EndP = [];
        aOtherActs_InitP = [];
        iAct = self.aKeys_Time.index(str(actModel.nTime)); #Index of Activity in TimeLine
        nInitT = int(actModel.rootAct.aTimes[0]); #Init Time of this Activity
        for i in range(iAct+1, len(self.aKeys_Time)): #Move from iAct+1 Forwards (up to End of TimeLine)
            nEndActT = int(self.aKeys_Time[i]); #End Time in Each Segment Activity            
            for actM in self.aActs_Time[str(nEndActT)]:
                if (actM.bCopySegment==False): 
                    nInitActT = int(actM.rootAct.aTimes[0]); 
                    if (nInitActT < actModel.nTime):
                        aOtherActs_EndP.append(actM);
        nTimePts = len(self.aKeys_Time);
        for i in range(nTimePts): #Move from End of TimeLine Backwards (up to nInitT)
            nEndActT = int(self.aKeys_Time[(nTimePts-1)-i]); #End Time in Each Segment Activity
            if (nEndActT <= nInitT):
                break;
            for actM in self.aActs_Time[str(nEndActT)]:
                if (actM.bCopySegment==False): 
                    nInitActT = int(actM.rootAct.aTimes[0]); 
                    if (nInitActT < nInitT):
                        aOtherActs_InitP.append(actM);
        #Check if those Collisions already have their segments         
        bEndPts = [];
        for actEndP in aOtherActs_EndP:
            bEnd = False;
            if not self.aActs_Time.has_key(str(actModel.nTime)): 
                bEndPts.append(bEnd);
                continue;
            for actM in self.aActs_Time[str(actModel.nTime)]:
                if (actM.bCopySegment==True) and (actM.sId.split(".")[0]==actEndP.sId.split(".")[0]): 
                    bEnd = True;
                    break;
            bEndPts.append(bEnd);
        bInitPts = [];
        for actInitP in aOtherActs_InitP:
            bInit = False;
            if not self.aActs_Time.has_key(str(nInitT)):
                bInitPts.append(bInit);
                continue;
            for actM in self.aActs_Time[str(nInitT)]:
                if (actM.bCopySegment==True) and (actM.sId.split(".")[0]==actInitP.sId.split(".")[0]): 
                    bInit = True;
                    break;
            bInitPts.append(bInit);
#################################################################################################################
        #Look for Init-Points for those Collisions that Don't have a Segment
        aInitSgm_EndP = [];
        aInitSgm_EndP2 = [];
        aSgm_EndP = [];
        for i in range(len(aOtherActs_EndP)): #For each Collision (without Segment)
#            if bEndPts[i]==False: #If it Doesn't have a Segment
#            iPt = self.aKeys_Time.index(str(aOtherActs_EndP[i].nTime));
            iPt = iAct;
            bEarlierCollision = False;
            for j in range(len(self.aKeys_Time[:iPt])): #Check if it has an earlier Collision (to use as Init-Point)
                bEarlierCollision = False;
                for actM in self.aActs_Time[self.aKeys_Time[iPt-(j+1)]]:
                    if (actM.bCopySegment==True) and (actM.sId.split(".")[0]==aOtherActs_EndP[i].sId.split(".")[0]):
                        aInitSgm_EndP.append(actM.nTime);
                        aInitSgm_EndP2.append(actM.rootAct.name);
                        aSgm_EndP.append(str(actM.nTime)+"-"+str(actModel.nTime));
                        bEarlierCollision = True;
                        break;
                if bEarlierCollision:
                    break;
            if bEarlierCollision==False:
                aInitSgm_EndP.append(int(aOtherActs_EndP[i].rootAct.aTimes[0]));
                aInitSgm_EndP2.append("T_Init_"+aOtherActs_EndP[i].rootAct.name);
                aSgm_EndP.append(aOtherActs_EndP[i].rootAct.aTimes[0]+"-"+str(actModel.nTime));
        aInitSgm_InitP = [];
        aInitSgm_InitP2 = [];
        aSgm_InitP = [];
        for i in range(len(aOtherActs_InitP)):
#            if bInitPts[i]==False: #If it Doesn't have a Segment
#            iPt = self.aKeys_Time.index(str(aOtherActs_InitP[i].nTime));
            iPt = self.aKeys_Time.index(actModel.rootAct.aTimes[0]);
            bEarlierCollision = False;
            for j in range(len(self.aKeys_Time[:iPt])): #Check if it has an earlier Collision (to use as Init-Point)
                bEarlierCollision = False;
                for actM in self.aActs_Time[self.aKeys_Time[iPt-(j+1)]]:
                    if (actM.bCopySegment==True) and (actM.sId.split(".")[0]==aOtherActs_InitP[i].sId.split(".")[0]):
                        aInitSgm_InitP.append(actM.nTime);
                        aInitSgm_InitP2.append(actM.rootAct.name);
                        aSgm_InitP.append(str(actM.nTime)+"-"+actModel.rootAct.aTimes[0]);
                        bEarlierCollision = True;
                        break;
                if bEarlierCollision:
                    break;
            if bEarlierCollision==False:
                aInitSgm_InitP.append(int(aOtherActs_InitP[i].rootAct.aTimes[0]));
                aInitSgm_InitP2.append("T_Init_"+aOtherActs_InitP[i].rootAct.name);
                aSgm_InitP.append(aOtherActs_InitP[i].rootAct.aTimes[0]+"-"+actModel.rootAct.aTimes[0]);
        #Print Test Results
        aNames_EndP = [];
        aNames_initP = [];
        for actM in aOtherActs_EndP:
            aNames_EndP.append(actM.rootAct.name);
        for actM in aOtherActs_InitP:
            aNames_initP.append(actM.rootAct.name);
        print "---Further Collisions from:", actModel.rootAct.name;
        print iAct+1, len(self.aKeys_Time), self.aKeys_Time, actModel.nTime, self.aKeys_Time.index(str(actModel.nTime));
        print "END-Pt Collisions";
        print "aOtherActs_EndP (Colliding With These Activities):", aNames_EndP;
        print "bEndPts (Segment Already Found in These Activities?):", bEndPts;
        print "aInitSgm_EndP2 (Segment Names):", aInitSgm_EndP2;
        print "aInitSgm_EndP (Segment INIT TimePt):", aInitSgm_EndP;
        print "aSgm_EndP (FULL Segment TimePt):", aSgm_EndP;
        print "---"
        print "INIT-Pt Collisions";
        print "aOtherActs_InitP (Colliding With These Activities):", aNames_initP;
        print "bInitPts (Segment Already Found in These Activities?):", bInitPts;
        print "aInitSgm_InitP2 (Segment Names):", aInitSgm_InitP2;
        print "aInitSgm_InitP (Segment INIT TimePt):", aInitSgm_InitP;
        print "aSgm_InitP (FULL Segment TimePt):", aSgm_InitP;
        print "";
            
    
    def addActToBB(self, actModel):
        sT = str(actModel.nTime);
        if self.aActs_Time.has_key(sT): #If Existing Time Point
            self.aActs_Time[sT].append(actModel);
        else: #If NEW Time Point
            self.aActs_Time[sT] = [actModel];
            self.addKeyTime(sT);
    
    def addXmlActs_ToBB(self):
#        newAct = self.getSegmentAct(self.inputSim.aActivities[0], 208);
#        self.inputSim.aActivities.append(newAct);
        #Insert all XML Activities into the timeline of HMM BB
        for i in range(len(self.inputSim.aActivities)): #For all XML Activities
#            print self.inputSim.aActivities[i].nTime;
            self.addActToBB(self.inputSim.aActivities[i]);
            self.addActCollisions(self.inputSim.aActivities[i]);
#            self.addFurtherActCollisions(self.inputSim.aActivities[i]);
        print "TEST ---> Add Further Collisons:"
        for i in range(len(self.inputSim.aActivities)): #For all XML Activities
            self.addFurtherActCollisions(self.inputSim.aActivities[i]);
        #Add & Link All Activities to the Graph
        print self.aKeys_Time;
        for sK in self.aKeys_Time:
            self.addNodesToGraph(sK);
            self.linkActivities(sK);
            
    def addKeyTime(self, sTime):
        for i in range(1,len(self.aKeys_Time)+1):
            if int(sTime) > int(self.aKeys_Time[-i]):                
                k = len(self.aKeys_Time)-i;
                self.aKeys_Time.insert(k+1, sTime);
                return;
        #Else (if sTime < self.aKeys_Time[0])
        self.aKeys_Time.insert(0, sTime);
    
    def addNodesToGraph(self, sTime):
        for i in range(len(self.aActs_Time[sTime])): #For each Act[t]
            aActT1 = self.aActs_Time[sTime][i];
            sAct1 = aActT1.getKeyTexts();
            if self.inputSim.bReadXML==True:
                print "Adding:", aActT1.sId;
                aActT1.makeXmlGraph(self.graph);
            else:
                nPrior = self.inputSim.aPriors[self.getIndex(sAct1)];
                aActT1.makeRndGraph(self.graph, nPrior);
            
    def linkActivities(self, sTime):
        for i in range(len(self.aActs_Time[sTime])): #For each Act[t]
            aActT1 = self.aActs_Time[sTime][i];
            sAct1 = aActT1.getKeyTexts();
            sPastTime = self.getPastKey(sTime);
            bPrior = (True if sPastTime==None else False);
            [aActs, aProbs] = self.getPastLinks(sPastTime, sAct1, bPrior);
#            [aActsM, aProbsM] = self.getMarkovLinks(sTime, sAct1);
#            if len(aActsM)>0:
#                if len(aActs)==0:
#                    aActs = aActsM;
#                    aProbs = aProbsM;                   
#                else:
#                    aActs.extend(aActsM);
#                    aProbs.extend(aProbsM);
            if len(aActs)>1:
                aProbs = self.getNoisyOR(aProbs);
            self.graph.addnode(aActT1.rootAct, aActs, aProbs);
            
    def getPastKey(self, sTime):
        iKey = self.aKeys_Time.index(sTime)-1;
        if iKey<0:
            return None;
        else:
            return self.aKeys_Time[iKey];

    def getPastLinks(self, sT0, sAct1, bPrior=False):
        aActs, aProbs = [], [];
        nIndexA1 = self.getIndex(sAct1);
        if bPrior:
            #Send a Prior
            aProbs.append(self.inputSim.aPriors[nIndexA1]);
            return [aActs, aProbs];
        for j in range(len(self.aActs_Time[sT0])): #For each Act[t-1]
            actT0 = self.aActs_Time[sT0][j];
            sAct0 = actT0.getKeyTexts();
            nIndexA0 = self.getIndex(sAct0);
            if self.getMatch(sAct0, sAct1):
                #Send a Transition
                aProbs.append(self.inputSim.aTrans[nIndexA0][nIndexA1]);
                aActs.append(actT0.rootAct);
        if aActs==[]:
            #Send a Prior
            aProbs.append(self.inputSim.aPriors[nIndexA1]);
        return [aActs, aProbs];
    
    def getMarkovLinks(self, sT0, sAct1):
        aActs, aProbs = [], [];
        nIndexA1 = self.getIndex(sAct1);
        for j in range(len(self.aActs_Time[sT0])): #For each Act[t]
            actT0 = self.aActs_Time[sT0][j];
            sAct0 = actT0.getKeyTexts();
            if sAct0==sAct1:
                continue;
            nIndexA0 = self.getIndex(sAct0);
            if self.getMatch(sAct0, sAct1) and self.getMatch(sAct1, sAct0):
                #Send Markov Correlation
                aProbs.append(self.inputSim.aMarkovM[nIndexA0][nIndexA1]);
                aActs.append(actT0.rootAct);
        return [aActs, aProbs];
                
                
                
                    